//*******************************************************************************
//reflect sample
//honghiepcao@gmail.com
//01.08.2013
// ******************************************************************************
package org.fullhappy;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.csc.cscip.ux.common.util.Utility;

public class ParseEngine implements Serializable {

	private static final Logger logger = LoggerFactory.getLogger(UXParseTools.class);
	private static final long serialVersionUID = 1L;

	public static Object getValueFromPath(Object source, String path) throws ParseException {

		String[] pathItems = path.split(Pattern.quote("."));
		Object currentObject = source;
		Class currentClass = currentObject.getClass();
		Field field = null;
		String catchPath = "";
		for (String pathItem : pathItems) {
			catchPath += "." + pathItem;
			String index = null;
			if (pathItem.contains(":")) {
				index = pathItem.split(":")[1];
				pathItem = pathItem.split(":")[0];
			}
			try {
				field = getField(currentClass, pathItem);
			} catch (SecurityException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			} catch (NoSuchFieldException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			}

			field.setAccessible(true);

			try {
				currentObject = field.get(currentObject);
			} catch (IllegalArgumentException e) {
				return null;
			} catch (IllegalAccessException e) {
				return null;
			}
			if (currentObject == null) {
				return null;
			}
			if (currentObject instanceof Collection && null != index) {
				if (!((Collection) currentObject).isEmpty()) {
					ArrayList list = new ArrayList((Collection) currentObject);
					if ("first".equalsIgnoreCase(index)) {
						currentObject = list.get(0);
					} else if ("last".equalsIgnoreCase(index)) {
						currentObject = list.get(list.size() - 1);
					} else {
						try {
							currentObject = list.get(Integer.parseInt(index));
						} catch (NumberFormatException nfe) {
							throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
						} catch (IndexOutOfBoundsException e) {
							throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
						}
					}
				} else {
					return null;
				}
			}
			currentClass = currentObject.getClass();
		}

		return currentObject;
	}

	public static void setValueFromPath(Object source, Object value, String path) throws ParseException, IllegalArgumentException {

		String[] pathItems = path.split(Pattern.quote("."));
		Object currentObject = source;
		Class currentClass = currentObject.getClass();
		Field field = null;
		String catchPath = "";
		for (String pathItem : pathItems) {
			catchPath += "." + pathItem;
			String index = null;
			if (pathItem.contains(":")) {
				index = pathItem.split(":")[1];
				pathItem = pathItem.split(":")[0];
			}
			try {
				field = getField(currentClass, pathItem);
			} catch (SecurityException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			} catch (NoSuchFieldException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			}

			field.setAccessible(true);

			if (path.equals(catchPath.substring(1))) {
				try {
					field.set(currentObject, value);
				} catch (IllegalArgumentException iae) {
					try {
						value = parseValue(field.getType(), value);
						field.set(currentObject, value);
					} catch (Exception e) {
						throw new IllegalArgumentException("can't parse because " + value.getClass() + " " + value + " not instanceof " + field.getType() + " ");
					}
				} catch (IllegalAccessException e) {
					throw new ParseException("not parse " + value.getClass() + " " + value + " to " + field.getType() + " ", -1);
				}
				return;
			}

			Object childObject = null;
			try {
				childObject = field.get(currentObject);
			} catch (IllegalArgumentException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			} catch (IllegalAccessException e) {
				throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
			}
			if (childObject == null) {
				if (Collection.class.isAssignableFrom(field.getType())) {
					childObject = new ArrayList(1);
				} else {
					try {
						childObject = field.getType().newInstance();
					} catch (InstantiationException e) {
						throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
					} catch (IllegalAccessException e) {
						throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
					}
				}

				try {
					field.set(currentObject, childObject);
				} catch (IllegalArgumentException iae) {
					throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
				} catch (IllegalAccessException e) {
					throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
				}

			}
			currentObject = childObject;

			if (currentObject instanceof Collection && null != index) {
				if (!((Collection) currentObject).isEmpty()) {
					ArrayList list = new ArrayList((Collection) currentObject);
					if ("first".equalsIgnoreCase(index)) {
						currentObject = list.get(0);
					} else if ("last".equalsIgnoreCase(index)) {
						currentObject = list.get(list.size() - 1);
					} else {
						int intIndex = 0;
						try {
							intIndex = Integer.parseInt(index);
						} catch (NumberFormatException nfe) {
							throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
						}
						if (0 <= intIndex && intIndex < list.size()) {
							currentObject = list.get(intIndex);
						} else {
							Type type = field.getGenericType();
							Type[] types = ((ParameterizedType) type).getActualTypeArguments();
							if (null != types && types.length > 0) {
								currentClass = (Class) types[0];
								Object o = null;
								try {
									o = currentClass.newInstance();
								} catch (InstantiationException e1) {
									throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
								} catch (IllegalAccessException e1) {
									throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
								}
								((Collection) currentObject).add(o);
								currentObject = o;
							}
						}

					}
				} else {
					Type type = field.getGenericType();
					Type[] types = ((ParameterizedType) type).getActualTypeArguments();
					if (null != types && types.length > 0) {
						currentClass = (Class) types[0];
						Object o = null;
						try {
							o = currentClass.newInstance();
						} catch (InstantiationException e) {
							throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
						} catch (IllegalAccessException e) {
							throw new ParseException("can't parse because path \"" + catchPath.substring(1) + "\"  invalid ", -1);
						}
						((Collection) currentObject).add(o);
						currentObject = o;
					}
				}
			}
			currentClass = currentObject.getClass();
		}

	}

	private static Field getField(Class clazz, String fieldName) throws NoSuchFieldException {
		try {
			return clazz.getDeclaredField(fieldName);
		} catch (NoSuchFieldException e) {
			Class superClass = clazz.getSuperclass();
			if (superClass == null) {
				throw e;
			} else {
				return getField(superClass, fieldName);
			}
		}
	}

	private static Object parseValue(Class targetClass, Object sourceValue) throws Exception {
		String methodName = "parse" + sourceValue.getClass().getSimpleName() + "To" + targetClass.getSimpleName();
		Method method = null;
		try {
			method = UXParseTools.class.getDeclaredMethod(methodName, sourceValue.getClass());
		} catch (Exception e) {
			throw e;
		}
		Object object = null;
		try {
			object = method.invoke(null, sourceValue);
		} catch (Exception e) {
			throw e;
		}
		return object;
	}

	@SuppressWarnings("unused")
	private static Date parseStringToDate(String str) {
		try {
			return Utility.getDateFromString(str);
		} catch (ParseException e) {
			return null;
		}
	}

}